package com.vinjogames.entities;

import javax.microedition.khronos.opengles.GL10;

import android.graphics.PointF;

import com.vinjogames.gfx.Gun;
import com.vinjogames.gfx.Image;
import com.vinjogames.gfx.SpriteSheet;
import com.vinjogames.util.GameConVars;
import com.vinjogames.util.PointFFlagged;
import com.vinjogames.util.Preferences;
import com.vinjogames.util.UnitUniqueConstants;

public class Unit extends AttackObject {
	public static final float MAX_FORCE = 15.0f;
	public static final float MIN_FORCE = 1.0f;

	private int unitType;
	private int foodCost;
	private int APUsedToMove;
	private float maxMovement;
	private float movementUsed;
	private float movementRemaining;
	private float distancePerAP;
	private float tempShadowLocation;
	private float movePerAP;
	private float startX;
	private Resource capturableResource;
	protected PointFFlagged[] path;
	protected SpriteSheet ss;
	protected PointFFlagged furthestPointOfMovement;
	
	private boolean isFloating;
	private boolean needsNewFurthestPoint;
	
	public Unit() {
		super(GameConVars.OBJECT_UNIT, 0);
		foodCost = 0;
		maxMovement = 0;
		movementUsed = 0;
		movementRemaining = 0;
		distancePerAP = 0;
		startX = 0;
		myPlayer = null;
		hasFired = false;
		
		isFloating = false;
		needsNewFurthestPoint = false;
	}

	public Unit(int type, Player p) {
		super(GameConVars.OBJECT_UNIT, 0);
		ss = SpriteSheet.getInstance();
		unitType = type;
		turnIntoType(unitType, p);
		hasFired = false;
		direction = myPlayer.isFirstPlayer();
		
		isFloating = false;
	}

	public PointFFlagged getFurthestPoint(GameMap map) {
		float x = -1f;

		if((getPlayer().isMoveAfterFire() && hasFired) || !hasFired) {
			if(direction) {
				if(mLocation.x  + (maxMovement - movementUsed) > map.getSize() - mImage.getWidth() * 0.5f - GameConVars.UNIT_ROTATION_CHECK)
					x = map.getSize() - mImage.getWidth() * 0.5f - GameConVars.UNIT_ROTATION_CHECK;
				else x = mLocation.x + (maxMovement - movementUsed);
			} else {
				if(mLocation.x  - (maxMovement - movementUsed) < mImage.getWidth() * 0.5f + GameConVars.UNIT_ROTATION_CHECK)
					x = mImage.getWidth() * 0.5f + GameConVars.UNIT_ROTATION_CHECK;
				else x = mLocation.x - (maxMovement - movementUsed); 
			}
			furthestPointOfMovement = map.getMap()[map.getClosestPoint(x)];
		}
		return furthestPointOfMovement;
	}

	public void switchDirection() {	
		if(direction)
			startX = mLocation.x + getMovementUsed();
		else
			startX = mLocation.x - getMovementUsed();
		direction = !direction; 
		mRotation = -mRotation;
		mGun.setAngle(-mGun.getAngle()); 
	}

	@Override
	public void clearInfo() {
		// TODO Auto-generated method stub

	}

	public void turnIntoType(int type, Player p) {
		myPlayer = p;

		switch(type){
		case GameConVars.UNIT_SMALL_TANK:
			mImage = new Image(GameConVars.GRAPHIC_TANKS[0], mGLWidth, mGLHeight);
			mImage.setAnimations(new int[]{0,1,7});
			initializeFromUnitStats(GameConVars.STATS_SMALL_TANK);
			mGun = new Gun(GameConVars.GRAPHIC_GUNS[0], Gun.MORTIMER, Gun.MORTIMERLENGTH);
			break;
		case GameConVars.UNIT_MEDIUM_TANK:
			mImage = new Image(GameConVars.GRAPHIC_TANKS[1], mGLWidth, mGLHeight);
			mImage.setAnimations(new int[]{0,0,0});
			initializeFromUnitStats(GameConVars.STATS_MEDIUM_TANK);
			mGun = new Gun(GameConVars.GRAPHIC_GUNS[1], Gun.PINKY, Gun.PINKYLENGTH);
			break;			
		case GameConVars.UNIT_HARVESTER_TANK:
			mImage = new Image(GameConVars.GRAPHIC_TANKS[2], mGLWidth, mGLHeight);
			mImage.setAnimations(new int[]{0,0,0});
			initializeFromUnitStats(GameConVars.STATS_HARVESTER_TANK);
			mGun = new Gun(GameConVars.GRAPHIC_GUNS[2], Gun.SNIPPY, Gun.SNIPPYLENGTH);
			break;
		case GameConVars.UNIT_SCOUT_TANK:
			mImage = new Image(GameConVars.GRAPHIC_TANKS[3], mGLWidth, mGLHeight);
			mImage.setAnimations(new int[]{0,1,7});
			initializeFromUnitStats(GameConVars.STATS_SCOUT_TANK);
			mGun = new Gun(GameConVars.GRAPHIC_GUNS[3], Gun.BLUEBOY, Gun.BLUEBOYLENGTH);
			break;
		case GameConVars.UNIT_ARTILLARY:
			mImage = new Image(GameConVars.GRAPHIC_TANKS[4], mGLWidth, mGLHeight);
			mImage.setAnimations(new int[]{0,0,0});
			initializeFromUnitStats(GameConVars.STATS_ARTILLARY);
			mGun = new Gun(GameConVars.GRAPHIC_GUNS[4], Gun.BLUEBOY, Gun.BLUEBOYLENGTH);
			break;			
		case GameConVars.UNIT_MARKET_TANK:
			mImage = new Image(GameConVars.GRAPHIC_TANKS[5], mGLWidth, mGLHeight);
			mImage.setAnimations(new int[]{0,0,0});
			initializeFromUnitStats(GameConVars.STATS_MARKET_TANK);
			mGun = new Gun(GameConVars.GRAPHIC_GUNS[5], Gun.BLUEBOY, Gun.BLUEBOYLENGTH);
			break;			
		case GameConVars.UNIT_FALCON_CHOPPER:
			mImage = new Image(GameConVars.GRAPHIC_TANKS[6], mGLWidth, mGLHeight);
			mImage.setAnimations(new int[]{0,0,0});
			initializeFromUnitStats(GameConVars.STATS_FALCON_CHOPPER);
			mGun = new Gun(GameConVars.GRAPHIC_GUNS[6], Gun.BLUEBOY, Gun.BLUEBOYLENGTH);
			break;
		case GameConVars.UNIT_BEHEMOTH_TANK:
			mImage = new Image(GameConVars.GRAPHIC_TANKS[7], mGLWidth, mGLHeight);
			mImage.setAnimations(new int[]{0,0,0});
			initializeFromUnitStats(GameConVars.STATS_BEHEMOTH_TANK);
			mGun = new Gun(GameConVars.GRAPHIC_GUNS[7], Gun.BLUEBOY, Gun.BLUEBOYLENGTH);
			break;
		case GameConVars.UNIT_SABO_TANK:
			mImage = new Image(GameConVars.GRAPHIC_TANKS[8], mGLWidth, mGLHeight);
			mImage.setAnimations(new int[]{0,0,0});
			//TODO: REMOVE DEFAULT AND PUT IN THE OTHER FOUR UNITS.
			initializeFromUnitStats(GameConVars.STATS_SABO_TANK);
			mGun = new Gun(GameConVars.GRAPHIC_GUNS[8], Gun.BLUEBOY, Gun.BLUEBOYLENGTH);
			break;
		default:
			mImage = new Image(GameConVars.GRAPHIC_TANKS[4], mGLWidth, mGLHeight);
			mImage.setAnimations(new int[]{0,0,0});
			initializeFromUnitStats(GameConVars.STATS_SCOUT_TANK);
			mGun = new Gun(GameConVars.GRAPHIC_GUNS[4], Gun.ABRAMS, Gun.ABRAMSLENGTH);
			break;
		}
		
	}

	public void draw(GL10 gl, GameMap map){
		gl.glPushMatrix();
		
		if(movementUsed > 0){//Need to move out to update unit calcs
			mRotation = recalculateRotation(map, mLocation.x);
//			getFurthestPoint(map);
		}
		
		rotate(mRotation);

		gl.glTranslatef(0, getGLHeight()*.5f, 0);
		getImage().setFlip(!direction);
		mGun.getImage().setFlip(!direction);
		mGun.draw(gl,mLocation, mRotation);
//		mImage.draw(gl, mLocation, currentState, 0, 1, 0, 1);
		super.draw(gl);
		gl.glPopMatrix();

		//Health
		drawBars(gl, mLocation.x, mLocation.y + mGLHeight/2, mGLWidth, mGLHeight, health/maxHealth, 0,1,0,1);
		//Movement
		drawBars(gl, mLocation.x, mLocation.y -barWidth + mGLHeight/2, mGLWidth, mGLHeight,1f-(this.movementUsed/this.maxMovement), 0,0,1,1); 

		if(Preferences.isDebug())drawCollisionBox(gl);
	}
	
	public boolean floating(GameMap map){
		if(map.getMap()[map.getClosestPoint(mLocation.x)].y == mLocation.y)
			return false;
		isFloating = true;
		return true;
	}
	
	public void drawShadow(GL10 gl, GameMap map, int ap){
		if(hasFired() && !getPlayer().isMoveAfterFire()) return;
		if(startX == 0){
			startX = mLocation.x;
		}
		
		tempShadowLocation = startX;
		PointF tempPoint;
		int apUsed = 0;
		
		if(direction)
			while(tempShadowLocation <= this.getFurthestPoint(map).x && ap - apUsed >= 0){
				if(tempShadowLocation > mLocation.x){
					tempPoint = map.getMap()[map.getClosestPoint(tempShadowLocation)];
					
					gl.glPushMatrix();
					float tempRotate = recalculateRotation(map, tempPoint.x);
					mImage.rotate(tempRotate);
					gl.glTranslatef(0, getGLHeight()*.5f, 0);
	
					getImage().setFlip(!direction);
					mGun.getImage().setFlip(!direction);
	
	
					mGun.drawShadow(gl,tempPoint, tempRotate);
					super.draw(gl, tempPoint, 1, 1, 1, .5f);
					gl.glPopMatrix();
				}
				
				tempShadowLocation += movePerAP;
				++apUsed;
			}
		
		else 
			while(tempShadowLocation >= this.getFurthestPoint(map).x - GameConVars.UNIT_MOVE_PER_TICK && ap - apUsed >= 0){
				if(tempShadowLocation + GameConVars.UNIT_MOVE_PER_TICK < mLocation.x){
					tempPoint = map.getMap()[map.getClosestPoint(tempShadowLocation)];
					
					gl.glPushMatrix();
					float tempRotate = recalculateRotation(map, tempPoint.x);
					mImage.rotate(tempRotate);
					gl.glTranslatef(0, getGLHeight()*.5f, 0);
	
					getImage().setFlip(!direction);
					mGun.getImage().setFlip(!direction);
	
	
					mGun.drawShadow(gl,tempPoint, tempRotate);
					super.draw(gl, tempPoint, 1, 1, 1, .5f);
					gl.glPopMatrix();
				}
				
				tempShadowLocation -= movePerAP;
				++apUsed;
			}		
	}
	
	public void drawShadow(GL10 gl, GameMap map, PointF shadowLocation){
		if(Math.abs(shadowLocation.x - mLocation.x) <= .5f) return;	//Don't draw if .5f close, parse out to GCV
		if(hasFired() && !getPlayer().isMoveAfterFire()) return;
		
		tempShadowLocation = mLocation.x;
		PointF tempPoint;
		
		if(direction)
			while(tempShadowLocation < shadowLocation.x && tempShadowLocation < mLocation.x + (movePerAP - APUsedToMove * movePerAP)){
				if(Math.abs(recalculateRotation(map, map.getMap()[map.getClosestPoint(tempShadowLocation)].x)) < 45)
					tempShadowLocation = tempShadowLocation + GameConVars.UNIT_MOVE_PER_TICK > shadowLocation.x ? tempShadowLocation = shadowLocation.x: tempShadowLocation + GameConVars.UNIT_MOVE_PER_TICK;
//					tempShadowLocation = tempShadowLocation + movePerAP > shadowLocation.x ? tempShadowLocation = shadowLocation.x: tempShadowLocation + movePerAP;
				else break;
			}
		else
			while(tempShadowLocation > shadowLocation.x){
				if(Math.abs(recalculateRotation(map, map.getMap()[map.getClosestPoint(tempShadowLocation)].x)) < 45)
					tempShadowLocation = tempShadowLocation - GameConVars.UNIT_MOVE_PER_TICK < shadowLocation.x ? tempShadowLocation = shadowLocation.x: tempShadowLocation - GameConVars.UNIT_MOVE_PER_TICK;
				else break;
			}
		
		if(Math.abs(tempShadowLocation - mLocation.x) <= .5f) return;
		
		tempPoint = map.getMap()[map.getClosestPoint(tempShadowLocation)];
		
		gl.glPushMatrix();
		float tempRotate = recalculateRotation(map, tempPoint.x);
		mImage.rotate(tempRotate);
		gl.glTranslatef(0, getGLHeight()*.5f, 0);

		getImage().setFlip(!direction);
		mGun.getImage().setFlip(!direction);


		mGun.drawShadow(gl,tempPoint, tempRotate);
		super.draw(gl, tempPoint, 1, 1, 1, .5f);
		gl.glPopMatrix();
	}
	
	public float recalculateRotation(GameMap map, float x){
		if(x <= GameConVars.UNIT_ROTATION_CHECK){
			x = GameConVars.UNIT_ROTATION_CHECK;
		}
		if(x >= map.getSize() - GameConVars.UNIT_ROTATION_CHECK){
		    x = map.getSize() - GameConVars.UNIT_ROTATION_CHECK;
		}

		int lo = map.getClosestPoint(x - GameConVars.UNIT_ROTATION_CHECK);
		int hi = map.getClosestPoint(x + GameConVars.UNIT_ROTATION_CHECK);
		
		if(lo == hi){
			return 0;
		}
		PointFFlagged[] points = map.getMap();
		return (direction ? 1 : -1) * (float) Math.toDegrees(Math.atan((points[hi].getY() - points[lo].getY()) / 
														               (points[hi].getX() - points[lo].getX())));
	}
	
	public void update(float time, GameMap gm){
		if(needsNewFurthestPoint){
			getFurthestPoint(gm);
			needsNewFurthestPoint = false;
		}
		if(isFloating){
			for(int t = 0; t < time * 0.01; ++t){
				//Floating
				if(gm.getMap()[gm.getClosestPoint(mLocation.x)].y < mLocation.y){
					if(gm.getMap()[gm.getClosestPoint(mLocation.x)].y < mLocation.y + GameConVars.SLIDE_THRESHOLD)
						setY(mLocation.y - GameConVars.SLIDE_THRESHOLD);
					else setY(gm.getMap()[gm.getClosestPoint(mLocation.x)].y);
					mRotation = recalculateRotation(gm, mLocation.x);
				}
				//Sliding
				else if(mRotation > 45){
					//Facing Right, Slide Left
					if(direction){
						setLocation(gm.getMap()[gm.getClosestPoint(mLocation.x -.01f)]);
					}
					//Facing Left, Slide Right
					else{
						setLocation(gm.getMap()[gm.getClosestPoint(mLocation.x +.01f)]);
					}
					mRotation = recalculateRotation(gm, mLocation.x);
				}
				else if (mRotation < -45){
					//Facing Right, Slide Right
					if(direction){
						setLocation(gm.getMap()[gm.getClosestPoint(mLocation.x +.01f)]);
					}
					//Facing Left, Slide Left
					else{
						setLocation(gm.getMap()[gm.getClosestPoint(mLocation.x -.01f)]);
					}
					mRotation = recalculateRotation(gm, mLocation.x);
				}
				
				else isFloating = false;
			}
		}
		rotate(mRotation);
	}
	
	private void initializeFromUnitStats(UnitUniqueConstants stats) {
		this.attackDamage = stats.DEFAULT_WEAPON_DAMAGE;
		this.attackForceMax = stats.DEFAULT_POWER;
		this.attackVariance = 5*stats.DEFAULT_ACCURACY;
		this.health = stats.DEFAULT_HEALTH;
		this.movePerAP = stats.DEFAULT_MOVE_PER_AP;
		this.maxMovement = stats.DEFAULT_MOVE_PER_AP * stats.DEFAULT_MOVE_AP_TOTAL;
		this.movementRemaining = 0;
		this.distancePerAP = stats.DEFAULT_MOVE_PER_AP;
		this.armor = stats.DEFAULT_ARMOR;
		this.foodCost = stats.DEFAULT_FOOD_COST;
		this.maxHealth = this.health;
		this.attackCost = stats.DEFAULT_ATTACK_COST;
	}

	public void resetMovement() {
		movementUsed = 0;
		APUsedToMove = 0;
		movementRemaining = 0;
		startX = 0;
	}
	
	public void move(float x, float y, float distanceToMove){ 
		//Don't move if the tank has already fired or the player has no resources left.
		if((hasFired && !myPlayer.isMoveAfterFire()) ||
				(movementRemaining <= 0 && myPlayer.getResourcesRemaining() <= 0) ||
				movementUsed >= maxMovement) return;
		
		if(movementRemaining <= 0) {
			++APUsedToMove;
			myPlayer.purchaseGeneric(1);
			movementRemaining += distancePerAP;
		}
		
		mLocation.set(x, y);
		movementUsed += distanceToMove;
		movementRemaining -= distanceToMove;
	}
	
	public int getAPUsed(){ return APUsedToMove; }
	public float getMaxMovement(){ return maxMovement; }
	public float getMovementUsed(){ return movementUsed; }
	public Player getPlayer() { return myPlayer; }
	public void setNeedsNewFurthestPoint(boolean need){ needsNewFurthestPoint = need; }
	public void setCapturableResource(Resource r){ capturableResource = r; }
	public Resource getCaptureableResource(){ return capturableResource; }
}
